package com.runmonk.core.study.java_8_new_features;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * java8 新特性
 * <p>
 * Stream管道流
 * 它可以指定你希望对集合进行的操作，可以执行非常复杂的查找、过滤和映射数据等操作。
 * 使用Stream API 对集合数据进行操作，就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。
 * 简而言之， Stream API 提供了一种高效且易于使用的处理数据的方式。
 */
public class StreamTest {
    //定义一个集合
    final static Collection<Task> tasks = Arrays.asList(
            new Task(Status.OPEN, 5),
            new Task(Status.OPEN, 13),
            new Task(Status.CLOSED, 8)
    );


    public static void main(String[] args) {
        /**
         * 测试1
         * 首先，tasks集合被转换成steam表示；
         * 其次，在steam上的filter操作会过滤掉所有CLOSED的task；
         * 第三，mapToInt操作基于每个task实例的Task::getPoints方法将task流转换成Integer集合；
         * 最后，通过sum方法计算总和，得出最后的结果。
         */
        final long totalPointsOfOpenTasks = tasks
                .stream()
                .filter(task -> task.getStatus() == Status.OPEN)
                .mapToInt(Task::getPoints)
                .sum();

        System.out.println("Total points: " + totalPointsOfOpenTasks);

        /**
         * 测试2
         *这里我们使用parallel方法并行处理所有的task，并使用reduce方法计算最终的结果
         */
        final double totalPoints = tasks
                .stream()
                .parallel()
                .map(task -> task.getPoints()) // or map( Task::getPoints )
                .reduce(0, Integer::sum);

        System.out.println("并行处理---->>>>>>>>>Total points (all tasks): " + totalPoints);


        /**
         * 测试3
         *
         * 对集合中的元素进行分组（按照status进行分组）
         */
        // Group tasks by their status
        final Map<Status, List<Task>> map = tasks
                .stream()
                .collect(Collectors.groupingBy(Task::getStatus));
        System.out.println(map);

        /**
         * 测试4
         *
         * 计算任务点 占总任务点数的比重
         */
        // Calculate the weight of each tasks (as percent of total points)
        final Collection<String> result = tasks
                .stream()                                           // Stream< String >
                .mapToInt(Task::getPoints)                          // IntStream
                .asLongStream()                                     // LongStream
                .mapToDouble(points -> points / totalPoints)        // DoubleStream
                .boxed()                                            // Stream< Double >
                .mapToLong(weigth -> (long) (weigth * 100))         // LongStream
                .mapToObj(percentage -> percentage + "%")           // Stream< String>
                .collect(Collectors.toList());                      // List< String >

        System.out.println(result);

    }


    private enum Status {
        OPEN, CLOSED
    }

    ;

    private static final class Task {
        private final Status status;
        private final Integer points;

        Task(final Status status, final Integer points) {
            this.status = status;
            this.points = points;
        }

        public Integer getPoints() {
            return points;
        }

        public Status getStatus() {
            return status;
        }

        @Override
        public String toString() {
            return String.format("[%s, %d]", status, points);
        }
    }
}
